{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![MLU Logo](../data/MLU_Logo.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <a name=\"0\">Machine Learning Accelerator - Natural Language Processing - Lecture 2</a>\n",
    "\n",
    "## Logistic Regression Model and Threshold Calibration\n",
    "\n",
    "In this notebook, we go over the Logistic Regression method to predict the __isPositive__ field of our final dataset, while also having a look at how probability threshold calibration can help improve classifier's performance.\n",
    "\n",
    "1. <a href=\"#1\">Reading the dataset</a>\n",
    "2. <a href=\"#2\">Exploratory data analysis</a>\n",
    "3. <a href=\"#3\">Stop word removal and stemming</a>\n",
    "4. <a href=\"#4\">Train - Validation Split</a>\n",
    "5. <a href=\"#5\">Data processing with Pipeline and ColumnTransform</a>\n",
    "6. <a href=\"#6\">Fit the classifier</a>\n",
    "Find more details on the __LogisticRegression__ here: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html\n",
    "7. <a href=\"#7\">Test the classifier</a>\n",
    "8. <a href=\"#8\">Ideas for improvement: Probability threshold calibration (optional)</a> \n",
    "\n",
    "Overall dataset schema:\n",
    "* __reviewText:__ Text of the review\n",
    "* __summary:__ Summary of the review\n",
    "* __verified:__ Whether the purchase was verified (True or False)\n",
    "* __time:__ UNIX timestamp for the review\n",
    "* __log_votes:__ Logarithm-adjusted votes log(1+votes)\n",
    "* __isPositive:__ Whether the review is positive or negative (1 or 0)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. <a name=\"1\">Reading the dataset</a>\n",
    "(<a href=\"#0\">Go to top</a>)\n",
    "\n",
    "We will use the __pandas__ library to read our datasets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "df = pd.read_csv('../data/examples/AMAZON-REVIEW-DATA-CLASSIFICATION.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's look at the first five rows in the datasets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>reviewText</th>\n",
       "      <th>summary</th>\n",
       "      <th>verified</th>\n",
       "      <th>time</th>\n",
       "      <th>log_votes</th>\n",
       "      <th>isPositive</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>PURCHASED FOR YOUNGSTER WHO\\nINHERITED MY \"TOO...</td>\n",
       "      <td>IDEAL FOR BEGINNER!</td>\n",
       "      <td>True</td>\n",
       "      <td>1361836800</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>unable to open or use</td>\n",
       "      <td>Two Stars</td>\n",
       "      <td>True</td>\n",
       "      <td>1452643200</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Waste of money!!! It wouldn't load to my system.</td>\n",
       "      <td>Dont buy it!</td>\n",
       "      <td>True</td>\n",
       "      <td>1433289600</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>I attempted to install this OS on two differen...</td>\n",
       "      <td>I attempted to install this OS on two differen...</td>\n",
       "      <td>True</td>\n",
       "      <td>1518912000</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>I've spent 14 fruitless hours over the past tw...</td>\n",
       "      <td>Do NOT Download.</td>\n",
       "      <td>True</td>\n",
       "      <td>1441929600</td>\n",
       "      <td>1.098612</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                          reviewText  \\\n",
       "0  PURCHASED FOR YOUNGSTER WHO\\nINHERITED MY \"TOO...   \n",
       "1                              unable to open or use   \n",
       "2   Waste of money!!! It wouldn't load to my system.   \n",
       "3  I attempted to install this OS on two differen...   \n",
       "4  I've spent 14 fruitless hours over the past tw...   \n",
       "\n",
       "                                             summary  verified        time  \\\n",
       "0                                IDEAL FOR BEGINNER!      True  1361836800   \n",
       "1                                          Two Stars      True  1452643200   \n",
       "2                                       Dont buy it!      True  1433289600   \n",
       "3  I attempted to install this OS on two differen...      True  1518912000   \n",
       "4                                   Do NOT Download.      True  1441929600   \n",
       "\n",
       "   log_votes  isPositive  \n",
       "0   0.000000         1.0  \n",
       "1   0.000000         0.0  \n",
       "2   0.000000         0.0  \n",
       "3   0.000000         0.0  \n",
       "4   1.098612         0.0  "
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. <a name=\"2\">Exploratory data analysis</a>\n",
    "(<a href=\"#0\">Go to top</a>)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's look at the target distribution for our datasets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "isPositive\n",
       "1.0    43692\n",
       "0.0    26308\n",
       "Name: count, dtype: int64"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df[\"isPositive\"].value_counts()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Checking the number of missing values:    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "reviewText    12\n",
      "summary       15\n",
      "verified       0\n",
      "time           0\n",
      "log_votes      0\n",
      "isPositive     0\n",
      "dtype: int64\n"
     ]
    }
   ],
   "source": [
    "print(df.isna().sum())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have missing values in our text fields."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. <a name=\"3\">Text Processing: Stop words removal and stemming</a>\n",
    "(<a href=\"#0\">Go to top</a>)\n",
    "\n",
    "We will apply the text processing methods discussed in the class. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[nltk_data] Downloading package punkt to /home/studio-lab-\n",
      "[nltk_data]     user/nltk_data...\n",
      "[nltk_data]   Package punkt is already up-to-date!\n",
      "[nltk_data] Downloading package stopwords to /home/studio-lab-\n",
      "[nltk_data]     user/nltk_data...\n",
      "[nltk_data]   Package stopwords is already up-to-date!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Install the library and functions\n",
    "import nltk\n",
    "\n",
    "nltk.download('punkt')\n",
    "nltk.download('stopwords')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will create the stop word removal and text cleaning processes below. NLTK library provides a list of common stop words. We will use the list, but remove some of the words from that list (because those words are actually useful to understand the sentiment in the sentence)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import nltk, re\n",
    "from nltk.corpus import stopwords\n",
    "from nltk.stem import SnowballStemmer\n",
    "from nltk.tokenize import word_tokenize\n",
    "\n",
    "# Let's get a list of stop words from the NLTK library\n",
    "stop = stopwords.words('english')\n",
    "\n",
    "# These words are important for our problem. We don't want to remove them.\n",
    "excluding = ['against', 'not', 'don', \"don't\",'ain', 'aren', \"aren't\", 'couldn', \"couldn't\",\n",
    "             'didn', \"didn't\", 'doesn', \"doesn't\", 'hadn', \"hadn't\", 'hasn', \"hasn't\", \n",
    "             'haven', \"haven't\", 'isn', \"isn't\", 'mightn', \"mightn't\", 'mustn', \"mustn't\",\n",
    "             'needn', \"needn't\",'shouldn', \"shouldn't\", 'wasn', \"wasn't\", 'weren', \n",
    "             \"weren't\", 'won', \"won't\", 'wouldn', \"wouldn't\"]\n",
    "\n",
    "# New stop word list\n",
    "stop_words = [word for word in stop if word not in excluding]\n",
    "\n",
    "snow = SnowballStemmer('english')\n",
    "\n",
    "def process_text(texts): \n",
    "    final_text_list=[]\n",
    "    for sent in texts:\n",
    "        \n",
    "        # Check if the sentence is a missing value\n",
    "        if isinstance(sent, str) == False:\n",
    "            sent = \"\"\n",
    "            \n",
    "        filtered_sentence=[]\n",
    "        \n",
    "        sent = sent.lower() # Lowercase \n",
    "        sent = sent.strip() # Remove leading/trailing whitespace\n",
    "        sent = re.sub('\\s+', ' ', sent) # Remove extra space and tabs\n",
    "        sent = re.compile('<.*?>').sub('', sent) # Remove HTML tags/markups:\n",
    "        \n",
    "        for w in word_tokenize(sent):\n",
    "            # We are applying some custom filtering here, feel free to try different things\n",
    "            # Check if it is not numeric and its length>2 and not in stop words\n",
    "            if(not w.isnumeric()) and (len(w)>2) and (w not in stop_words):  \n",
    "                # Stem and add to filtered list\n",
    "                filtered_sentence.append(snow.stem(w))\n",
    "        final_string = \" \".join(filtered_sentence) #final string of cleaned words\n",
    " \n",
    "        final_text_list.append(final_string)\n",
    "        \n",
    "    return final_text_list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. <a name=\"4\">Train - Validation Split</a>\n",
    "(<a href=\"#0\">Go to top</a>)\n",
    "\n",
    "Let's split our dataset into training (90%) and validation (10%). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "X_train, X_val, y_train, y_val = train_test_split(df[[\"reviewText\", \"summary\", \"time\", \"log_votes\"]],\n",
    "                                                  df[\"isPositive\"],\n",
    "                                                  test_size=0.10,\n",
    "                                                  shuffle=True,\n",
    "                                                  random_state=324\n",
    "                                                 )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Processing the reviewText fields\n",
      "Processing the summary fields\n"
     ]
    }
   ],
   "source": [
    "print(\"Processing the reviewText fields\")\n",
    "X_train[\"reviewText\"] = process_text(X_train[\"reviewText\"].tolist())\n",
    "X_val[\"reviewText\"] = process_text(X_val[\"reviewText\"].tolist())\n",
    "\n",
    "print(\"Processing the summary fields\")\n",
    "X_train[\"summary\"] = process_text(X_train[\"summary\"].tolist())\n",
    "X_val[\"summary\"] = process_text(X_val[\"summary\"].tolist())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Our process_text() method in section 3 uses empty string for missing values."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. <a name=\"5\">Data processing with Pipeline and ColumnTransform</a>\n",
    "(<a href=\"#0\">Go to top</a>)\n",
    "\n",
    "In the previous examples, we have seen how to use pipeline to prepare a data field for our machine learning model. This time, we will focus on multiple fields: numeric and text fields. Find more details on __LogisticRegression__ here:\n",
    "https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html\n",
    "\n",
    "   * For the numerical features pipeline, the __numerical_processor__ below, we use a MinMaxScaler (don't have to scale features when using Decision Trees, but it's a good idea to see how to use more data transforms). If different processing is desired for different numerical features, different pipelines should be built - just like shown below for the two text features.\n",
    "   * For the text features pipeline, the __text_processor__ below, we use CountVectorizer() for the text fields.\n",
    "   \n",
    "The selective preparations of the dataset features are then put together into a collective ColumnTransformer, to be finally used in a Pipeline along with an estimator. This ensures that the transforms are performed automatically on the raw data when fitting the model and when making predictions, such as when evaluating the model on a validation dataset via cross-validation or making predictions on a test dataset in the future."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Grab model features/inputs and target/output\n",
    "numerical_features = ['time',\n",
    "                      'log_votes']\n",
    "\n",
    "text_features = ['summary',\n",
    "                 'reviewText']\n",
    "\n",
    "model_features = numerical_features + text_features\n",
    "model_target = 'isPositive'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: black;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 1ex;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;data_preprocessing&#x27;,\n",
       "                 ColumnTransformer(transformers=[(&#x27;numerical_pre&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;num_scaler&#x27;,\n",
       "                                                                   MinMaxScaler())]),\n",
       "                                                  [&#x27;time&#x27;, &#x27;log_votes&#x27;]),\n",
       "                                                 (&#x27;text_pre_0&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;text_vect_0&#x27;,\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=50))]),\n",
       "                                                  &#x27;summary&#x27;),\n",
       "                                                 (&#x27;text_pre_1&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;text_vect_1&#x27;,\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=150))]),\n",
       "                                                  &#x27;reviewText&#x27;)])),\n",
       "                (&#x27;logistic_regression&#x27;, LogisticRegression(C=0.1))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" ><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link \">i<span>Not fitted</span></span></label><div class=\"sk-toggleable__content \"><pre>Pipeline(steps=[(&#x27;data_preprocessing&#x27;,\n",
       "                 ColumnTransformer(transformers=[(&#x27;numerical_pre&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;num_scaler&#x27;,\n",
       "                                                                   MinMaxScaler())]),\n",
       "                                                  [&#x27;time&#x27;, &#x27;log_votes&#x27;]),\n",
       "                                                 (&#x27;text_pre_0&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;text_vect_0&#x27;,\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=50))]),\n",
       "                                                  &#x27;summary&#x27;),\n",
       "                                                 (&#x27;text_pre_1&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;text_vect_1&#x27;,\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=150))]),\n",
       "                                                  &#x27;reviewText&#x27;)])),\n",
       "                (&#x27;logistic_regression&#x27;, LogisticRegression(C=0.1))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-2\" type=\"checkbox\" ><label for=\"sk-estimator-id-2\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;data_preprocessing: ColumnTransformer<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.compose.ColumnTransformer.html\">?<span>Documentation for data_preprocessing: ColumnTransformer</span></a></label><div class=\"sk-toggleable__content \"><pre>ColumnTransformer(transformers=[(&#x27;numerical_pre&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;num_scaler&#x27;,\n",
       "                                                  MinMaxScaler())]),\n",
       "                                 [&#x27;time&#x27;, &#x27;log_votes&#x27;]),\n",
       "                                (&#x27;text_pre_0&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;text_vect_0&#x27;,\n",
       "                                                  CountVectorizer(binary=True,\n",
       "                                                                  max_features=50))]),\n",
       "                                 &#x27;summary&#x27;),\n",
       "                                (&#x27;text_pre_1&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;text_vect_1&#x27;,\n",
       "                                                  CountVectorizer(binary=True,\n",
       "                                                                  max_features=150))]),\n",
       "                                 &#x27;reviewText&#x27;)])</pre></div> </div></div><div class=\"sk-parallel\"><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-3\" type=\"checkbox\" ><label for=\"sk-estimator-id-3\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">numerical_pre</label><div class=\"sk-toggleable__content \"><pre>[&#x27;time&#x27;, &#x27;log_votes&#x27;]</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-4\" type=\"checkbox\" ><label for=\"sk-estimator-id-4\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;MinMaxScaler<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.preprocessing.MinMaxScaler.html\">?<span>Documentation for MinMaxScaler</span></a></label><div class=\"sk-toggleable__content \"><pre>MinMaxScaler()</pre></div> </div></div></div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-5\" type=\"checkbox\" ><label for=\"sk-estimator-id-5\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">text_pre_0</label><div class=\"sk-toggleable__content \"><pre>summary</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-6\" type=\"checkbox\" ><label for=\"sk-estimator-id-6\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;CountVectorizer<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html\">?<span>Documentation for CountVectorizer</span></a></label><div class=\"sk-toggleable__content \"><pre>CountVectorizer(binary=True, max_features=50)</pre></div> </div></div></div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-7\" type=\"checkbox\" ><label for=\"sk-estimator-id-7\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">text_pre_1</label><div class=\"sk-toggleable__content \"><pre>reviewText</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-8\" type=\"checkbox\" ><label for=\"sk-estimator-id-8\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;CountVectorizer<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html\">?<span>Documentation for CountVectorizer</span></a></label><div class=\"sk-toggleable__content \"><pre>CountVectorizer(binary=True, max_features=150)</pre></div> </div></div></div></div></div></div></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator  sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-9\" type=\"checkbox\" ><label for=\"sk-estimator-id-9\" class=\"sk-toggleable__label  sk-toggleable__label-arrow \">&nbsp;LogisticRegression<a class=\"sk-estimator-doc-link \" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.linear_model.LogisticRegression.html\">?<span>Documentation for LogisticRegression</span></a></label><div class=\"sk-toggleable__content \"><pre>LogisticRegression(C=0.1)</pre></div> </div></div></div></div></div></div>"
      ],
      "text/plain": [
       "Pipeline(steps=[('data_preprocessing',\n",
       "                 ColumnTransformer(transformers=[('numerical_pre',\n",
       "                                                  Pipeline(steps=[('num_scaler',\n",
       "                                                                   MinMaxScaler())]),\n",
       "                                                  ['time', 'log_votes']),\n",
       "                                                 ('text_pre_0',\n",
       "                                                  Pipeline(steps=[('text_vect_0',\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=50))]),\n",
       "                                                  'summary'),\n",
       "                                                 ('text_pre_1',\n",
       "                                                  Pipeline(steps=[('text_vect_1',\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=150))]),\n",
       "                                                  'reviewText')])),\n",
       "                ('logistic_regression', LogisticRegression(C=0.1))])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.impute import SimpleImputer\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "from sklearn.feature_extraction.text import CountVectorizer\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "\n",
    "### COLUMN_TRANSFORMER ###\n",
    "##########################\n",
    "\n",
    "# Preprocess the numerical features\n",
    "numerical_processor = Pipeline([\n",
    "    ('num_scaler', MinMaxScaler())\n",
    "])\n",
    "\n",
    "# Preprocess 1st text feature\n",
    "text_processor_0 = Pipeline([\n",
    "    ('text_vect_0', CountVectorizer(binary=True, max_features=50))\n",
    "])\n",
    "\n",
    "# Preprocess 2nd text feature (larger vocabulary)\n",
    "text_precessor_1 = Pipeline([\n",
    "    ('text_vect_1', CountVectorizer(binary=True, max_features=150))\n",
    "])\n",
    "\n",
    "# Combine all data preprocessors from above (add more, if you choose to define more!)\n",
    "# For each processor/step specify: a name, the actual process, and finally the features to be processed\n",
    "data_preprocessor = ColumnTransformer([\n",
    "    ('numerical_pre', numerical_processor, numerical_features),\n",
    "    ('text_pre_0', text_processor_0, text_features[0]),\n",
    "    ('text_pre_1', text_precessor_1, text_features[1])\n",
    "]) \n",
    "\n",
    "### PIPELINE ###\n",
    "################\n",
    "\n",
    "# Pipeline desired all data transformers, along with an estimator at the end\n",
    "# Later you can set/reach the parameters using the names issued - for hyperparameter tuning, for example\n",
    "pipeline = Pipeline([\n",
    "    ('data_preprocessing', data_preprocessor),\n",
    "    ('logistic_regression', LogisticRegression(penalty = 'l2',\n",
    "                              C = 0.1))\n",
    "                    ])\n",
    "\n",
    "# Visualize the pipeline\n",
    "# This will come in handy especially when building more complex pipelines, stringing together multiple preprocessing steps\n",
    "from sklearn import set_config\n",
    "set_config(display='diagram')\n",
    "pipeline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. <a name=\"6\">Fit the classifier</a>\n",
    "(<a href=\"#0\">Go to top</a>)\n",
    "\n",
    "We train our model by using __.fit()__ on our training dataset. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-2 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: black;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-2 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-2 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-2 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-2 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-2 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-2 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-2 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-2 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-2 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-2 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-2 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 1ex;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-2 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-2\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;data_preprocessing&#x27;,\n",
       "                 ColumnTransformer(transformers=[(&#x27;numerical_pre&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;num_scaler&#x27;,\n",
       "                                                                   MinMaxScaler())]),\n",
       "                                                  [&#x27;time&#x27;, &#x27;log_votes&#x27;]),\n",
       "                                                 (&#x27;text_pre_0&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;text_vect_0&#x27;,\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=50))]),\n",
       "                                                  &#x27;summary&#x27;),\n",
       "                                                 (&#x27;text_pre_1&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;text_vect_1&#x27;,\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=150))]),\n",
       "                                                  &#x27;reviewText&#x27;)])),\n",
       "                (&#x27;logistic_regression&#x27;, LogisticRegression(C=0.1))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-10\" type=\"checkbox\" ><label for=\"sk-estimator-id-10\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>Pipeline(steps=[(&#x27;data_preprocessing&#x27;,\n",
       "                 ColumnTransformer(transformers=[(&#x27;numerical_pre&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;num_scaler&#x27;,\n",
       "                                                                   MinMaxScaler())]),\n",
       "                                                  [&#x27;time&#x27;, &#x27;log_votes&#x27;]),\n",
       "                                                 (&#x27;text_pre_0&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;text_vect_0&#x27;,\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=50))]),\n",
       "                                                  &#x27;summary&#x27;),\n",
       "                                                 (&#x27;text_pre_1&#x27;,\n",
       "                                                  Pipeline(steps=[(&#x27;text_vect_1&#x27;,\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=150))]),\n",
       "                                                  &#x27;reviewText&#x27;)])),\n",
       "                (&#x27;logistic_regression&#x27;, LogisticRegression(C=0.1))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-11\" type=\"checkbox\" ><label for=\"sk-estimator-id-11\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;data_preprocessing: ColumnTransformer<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.compose.ColumnTransformer.html\">?<span>Documentation for data_preprocessing: ColumnTransformer</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>ColumnTransformer(transformers=[(&#x27;numerical_pre&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;num_scaler&#x27;,\n",
       "                                                  MinMaxScaler())]),\n",
       "                                 [&#x27;time&#x27;, &#x27;log_votes&#x27;]),\n",
       "                                (&#x27;text_pre_0&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;text_vect_0&#x27;,\n",
       "                                                  CountVectorizer(binary=True,\n",
       "                                                                  max_features=50))]),\n",
       "                                 &#x27;summary&#x27;),\n",
       "                                (&#x27;text_pre_1&#x27;,\n",
       "                                 Pipeline(steps=[(&#x27;text_vect_1&#x27;,\n",
       "                                                  CountVectorizer(binary=True,\n",
       "                                                                  max_features=150))]),\n",
       "                                 &#x27;reviewText&#x27;)])</pre></div> </div></div><div class=\"sk-parallel\"><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-12\" type=\"checkbox\" ><label for=\"sk-estimator-id-12\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">numerical_pre</label><div class=\"sk-toggleable__content fitted\"><pre>[&#x27;time&#x27;, &#x27;log_votes&#x27;]</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-13\" type=\"checkbox\" ><label for=\"sk-estimator-id-13\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;MinMaxScaler<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.preprocessing.MinMaxScaler.html\">?<span>Documentation for MinMaxScaler</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>MinMaxScaler()</pre></div> </div></div></div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-14\" type=\"checkbox\" ><label for=\"sk-estimator-id-14\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">text_pre_0</label><div class=\"sk-toggleable__content fitted\"><pre>summary</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-15\" type=\"checkbox\" ><label for=\"sk-estimator-id-15\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;CountVectorizer<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html\">?<span>Documentation for CountVectorizer</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>CountVectorizer(binary=True, max_features=50)</pre></div> </div></div></div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-16\" type=\"checkbox\" ><label for=\"sk-estimator-id-16\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">text_pre_1</label><div class=\"sk-toggleable__content fitted\"><pre>reviewText</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-17\" type=\"checkbox\" ><label for=\"sk-estimator-id-17\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;CountVectorizer<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html\">?<span>Documentation for CountVectorizer</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>CountVectorizer(binary=True, max_features=150)</pre></div> </div></div></div></div></div></div></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-18\" type=\"checkbox\" ><label for=\"sk-estimator-id-18\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;LogisticRegression<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.linear_model.LogisticRegression.html\">?<span>Documentation for LogisticRegression</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>LogisticRegression(C=0.1)</pre></div> </div></div></div></div></div></div>"
      ],
      "text/plain": [
       "Pipeline(steps=[('data_preprocessing',\n",
       "                 ColumnTransformer(transformers=[('numerical_pre',\n",
       "                                                  Pipeline(steps=[('num_scaler',\n",
       "                                                                   MinMaxScaler())]),\n",
       "                                                  ['time', 'log_votes']),\n",
       "                                                 ('text_pre_0',\n",
       "                                                  Pipeline(steps=[('text_vect_0',\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=50))]),\n",
       "                                                  'summary'),\n",
       "                                                 ('text_pre_1',\n",
       "                                                  Pipeline(steps=[('text_vect_1',\n",
       "                                                                   CountVectorizer(binary=True,\n",
       "                                                                                   max_features=150))]),\n",
       "                                                  'reviewText')])),\n",
       "                ('logistic_regression', LogisticRegression(C=0.1))])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Fit the Pipeline to training data\n",
    "pipeline.fit(X_train, y_train.values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. <a name=\"7\">Test the classifier</a>\n",
    "(<a href=\"#0\">Go to top</a>)\n",
    "\n",
    "Let's evaluate the performance of the trained classifier. We use __.predict()__ this time. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1995  610]\n",
      " [ 450 3945]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "         0.0       0.82      0.77      0.79      2605\n",
      "         1.0       0.87      0.90      0.88      4395\n",
      "\n",
      "    accuracy                           0.85      7000\n",
      "   macro avg       0.84      0.83      0.84      7000\n",
      "weighted avg       0.85      0.85      0.85      7000\n",
      "\n",
      "Accuracy (validation): 0.8485714285714285\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import confusion_matrix, classification_report, accuracy_score\n",
    "\n",
    "# Use the fitted pipeline to make predictions on the validation dataset\n",
    "val_predictions = pipeline.predict(X_val)\n",
    "print(confusion_matrix(y_val.values, val_predictions))\n",
    "print(classification_report(y_val.values, val_predictions))\n",
    "print(\"Accuracy (validation):\", accuracy_score(y_val.values, val_predictions))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8. <a name=\"8\">Ideas for improvement: Probability threshold calibration (optional)</a>\n",
    "(<a href=\"#0\">Go to top</a>)\n",
    "\n",
    "Besides tuning __LogisticRegression__ hyperparameter values, one other path to improve a classifier's performance is to dig deeper into how the classifier actually assigns class membership.\n",
    "\n",
    "**Binary predictions versus probability predictions.** We often use __classifier.predict()__ to examine classifier binary predictions, while in fact the outputs of most classifiers are real-valued, not binary. For most classifiers in sklearn, the method __classifier.predict_proba()__ returns class probabilities as a two-dimensional numpy array of shape (n_samples, n_classes) where the classes are lexicographically ordered. \n",
    "\n",
    "For our example, let's look at the first 5 predictions we made, in binary format and in real-valued probability format:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 1., 1., 1., 0.])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pipeline.predict(X_val)[0:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.03660104, 0.96339896],\n",
       "       [0.03771839, 0.96228161],\n",
       "       [0.0922567 , 0.9077433 ],\n",
       "       [0.01057444, 0.98942556],\n",
       "       [0.84385308, 0.15614692]])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pipeline.predict_proba(X_val)[0:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**How are the predicted probabilities used to decide class membership?** On each row of predict_proba output, the probabilities values sum to 1. There are two columns, one for each response class: column 0 - predicted probability that each observation is a member of class 0; column 1 - predicted probability that each observation is a member of class 1. From the predicted probabilities, choose the class with the highest probability.\n",
    "\n",
    "The key here is that a **threshold of 0.5** is used by default (for binary problems) to convert predicted probabilities into class predictions: class 0, if predicted probability is less than 0.5; class 1, if predicted probability is greater than 0.5.\n",
    "\n",
    "**Can we improve classifier performance by changing the classification threshold?** Let's **adjust** the classification threshold to influence the performance of the classifier. \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8.1 Threshold calibration to improve model accuracy\n",
    "\n",
    "We calculate the accuracy using different values for the classification threshold, and pick the threshold that resulted in the highest accuracy."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Highest Accuracy on Validation: 0.849 , Threshold for the highest Accuracy: 0.51\n"
     ]
    },
    {
     "data": {
      "image/png": "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0tt32LBhePDgAeLj440e+/79+/jrr7/wf//3f3rvuYoVK6J379562xb3c6A8YwJkJf7++2/s3bsXPXv2hBACaWlpSEtLQ//+/QFAr0361q1bqFmzZoHHK8o2pnJycoKLi4temVqtRlhYGDZs2IB3330Xu3btwp9//ok//vgDgKbjIgD8+++/yMnJKVJM48aNw65du3Du3Dk8fvwY3377Lfr37w93d/cC97O1tUV4eDg2btyo64MRFRUFDw8PdO3aVbfdjRs38Ouvv8LOzk7v1qhRIwAw6LuUe+SU1uTJk/H555/jjz/+QPfu3VGlShV06tQJhw4dKvT88mrXrh1+/vln3Qd7zZo1ERAQgNWrVxe43+3bt2Fra4sqVarolRd2nYrjxo0bEELAzc3N4Lr98ccfRbpmN27cwIkTJwz2r1ixIoQQBsfIe14AoFQqde8pU926dQsAivx3kffnazu+a3++9vrnTeglSYK7uztu375dYCw1atSAjU3+H/FFveYqlQpxcXFo1qwZ3n//fTRq1Ag1atTAtGnT8Pjx43yPr43P2O+qRo0aBcb/LEz5veaNTdvHsWXLlgbXJDo6WndNJEnCrl270LVrV8yePRstWrRAtWrVMG7cONy9e9fkeG7fvp3vddK+bsy///4LtVpt9G8yb1lxPwfKM44CsxLLli2DEALr1q3DunXrDF5fsWIFZs6cCYVCgWrVqhXaubEo22j/I9F2LNTKb/4TY6MvTp06hePHjyMqKgpDhw7Vlf/9999627m6ukKhUBSpU+bLL7+M9957DwsXLkTr1q2RkpKC119/vdD9AGD48OH47LPPsGbNGgwaNAibNm1CREQEFAqFbpuqVauiSZMm+OSTT4weQ/uhpmXsvG1tbTFhwgRMmDABaWlp2LlzJ95//3107doVV69ehZOTExwcHAyuLWD8+vbt2xd9+/ZFZmYm/vjjD0RGRuLll19G7dq1ERISYjTOKlWqIDs7G7dv39b7EE9JSTF+cZ5B1apVIUkS9u3bZ3QEXN4yY9esatWqcHR0NNrBVPt6adImKkV5DxaF9vrfunVLLwkSQiAlJUX3H31+sezfvx9qtTrfJMiUa964cWOsWbMGQgicOHECUVFRmDFjBhwdHTFp0qR84weA5ORkg9euX79e6r+Posj7PtLGtG7dOl0tc368vb11g0rOnz+Pn376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iIiIqKQ5qIC7yWWiBki2BKhq1apQKBQGNTM3b940qMEpSOvWrfHDDz/onru7u5t8TKVSCaVSWeSfSUTlg6uzvdwhEFmXMjQUXrYmMHt7ewQGBiI2NlavPDY2FqGhoUU+ztGjR+Hh4aF7HhISYnDMHTt2mHRMIir/nOxtceSDLjjyQRc42cteGU5kHdgEpjFhwgSEh4cjKCgIISEhWLJkCRITEzF27FgAmqappKQkrFy5EoBmhFft2rXRqFEjZGVl4YcffsD69euxfv163THHjx+Pdu3aYdasWejbty9++eUX7Ny5E/v375flHImIiOiJMjQbtKwJ0KBBg3D79m3MmDEDycnJCAgIQExMDLy9vQEAycnJenMCZWVl4e2330ZSUhIcHR3RqFEjbNmyBT169NBtExoaijVr1mDq1Kn44IMP4Ovri+joaAQHB5v9/IiIiCiXMtQEJgnBKVDzysjIgEqlQnp6OlxcXOQOh4hKwaPHORi67E8AwIoRreBgpyhkDyJ6Zrs/AfbOBlqOAnp+UeKHN+X7mw3fRGSV1ELg4OU7usdEZAZlqA+Q7GuBERERkZUoQ32AmAARERGReZShPkBMgIiIiMg82ARGREREVodNYERERGR1cjeByTz4gKPAiMhqOXLoO5F5aWuAcrKA7EeAnaNsoTABIiKr5GRvizMfd5M7DCLroqwISApA5Gj6AcmYALEJjIiIiMxDknL1A0qTNRQmQERERGQ+ZaQjNBMgIrJKjx7nYPjyPzF8+Z949DhH7nCIrEcZGQrPPkBEZJXUQmDPuVu6x0RkJqwBIiIiIqtTRmaDZgJERERE5qNtAmMNEBEREVkNbROYzH2AmAARERGR+WibwG6dBdKTZAuDCRARERGZz80zmvuLu4C5AcCRlbKEwQSIiIiIzCM9CTi59ulzoQZ+jZClJojD4InIKjnZ2+LKpz3lDoPIuty5CCDPtBMiB7hzCVB5mjUU1gARERGRebj6AlKe1ENSAK51zB4KEyAiIiIyD5Un0HueJukBNPe955q99gdgExgRERGZU4shgG8nTbOXax1Zkh+ACRARERGZm8pTtsRHi01gREREZHWYABEREZHVYQJEREREVocJEBEREVkdJkBERERkdZgAERERkdVhAkRERERWhwkQERERWR0mQERERGR1mAARERGR1WECRERERFaHa4EZIYQA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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline \n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Calculate the accuracy using different values for the classification threshold, \n",
    "# and pick the threshold that resulted in the highest accuracy.\n",
    "highest_accuracy = 0\n",
    "threshold_highest_accuracy = 0\n",
    "\n",
    "thresholds = np.arange(0,1,0.01)\n",
    "scores = []\n",
    "for t in thresholds:\n",
    "    # set threshold to 't' instead of 0.5\n",
    "    y_val_other = (pipeline.predict_proba(X_val)[:,1] >= t).astype(float)\n",
    "    score = accuracy_score(y_val, y_val_other)\n",
    "    scores.append(score)\n",
    "    if(score > highest_accuracy):\n",
    "        highest_accuracy = score\n",
    "        threshold_highest_accuracy = t\n",
    "print(\"Highest Accuracy on Validation:\", highest_accuracy, \\\n",
    "      \", Threshold for the highest Accuracy:\", threshold_highest_accuracy)   \n",
    "\n",
    "# Let's plot the accuracy versus different choices of thresholds\n",
    "plt.plot([0.5, 0.5], [np.min(scores), np.max(scores)], linestyle='--')\n",
    "plt.plot(thresholds, scores, marker='.')\n",
    "plt.title('Accuracy versus different choices of thresholds')\n",
    "plt.xlabel('Threshold')\n",
    "plt.ylabel('Accuracy')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8.2 Threshold calibration to improve model F1 score\n",
    "\n",
    "Similarly, various choices of classification thresholds would affect the Precision and Recall metrics. Precision and Recall are usually trade offs of each other, so when you can improve both at the same time, your model's overall performance is undeniably improved. To choose a threshold that balances Precision and Recall, we can plot the Precision-Recall curve and pick the point with the highest F1 score. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%matplotlib inline \n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.metrics import precision_recall_curve\n",
    "\n",
    "# Calculate the precision and recall using different values for the classification threshold\n",
    "val_predictions_probs = pipeline.predict_proba(X_val)\n",
    "precisions, recalls, thresholds = precision_recall_curve(y_val, val_predictions_probs[:, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using the Precision and Recall values from the curve above, we calculate the F1 scores using:\n",
    "\n",
    "$$\\text{F1_score} = \\frac{2*(\\text{Precision} * \\text{Recall})}{(\\text{Precision} + \\text{Recall})}$$\n",
    "\n",
    "and pick the threshold that gives the highest F1 score."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Highest F1 score on Validation: 0.8824633042710518 , Threshold for the highest F1 score: 0.4703618308923428\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHFCAYAAAAOmtghAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAABSPklEQVR4nO3deXgT1foH8G+aNOlGAxQoS0tZZCk7tlIpFxCQXQR/IghclgJeKiCyiSAKBeVWUBFEWa5IuXARUSyIWIGyVaCgUoooRUG2UmhZpWXpmpzfH7XR0CRNSpJJJ9/P8+QhPXNm8mZoJ2/OnEUhhBAgIiIikgkPqQMgIiIisicmN0RERCQrTG6IiIhIVpjcEBERkawwuSEiIiJZYXJDREREssLkhoiIiGSFyQ0RERHJCpMbIiIikhUmNzKydu1aKBQKk4/p06cb6m3fvh0jRoxAy5Yt4enpCYVCYdPr3Lx5E7NmzUKzZs3g6+sLrVaLpk2bYvjw4Thx4oS93xY5iUKhQExMjOHnkt+nCxcuGNV7/fXXUbduXahUKlSuXBkAUFBQgOjoaNSqVQtKpRJt2rRxWty2Sk5ORkxMDG7fvm23Y8bExEChUODGjRt2O+YTTzyBJ554wm7Hk8KyZcvwyCOPQK1WQ6FQmD3nlv5P6tWrh6eeesqxgdrA3vFcuHABCoUCa9euLbNuye8ZlU0ldQBkf3FxcWjatKlRWe3atQ3Pt2zZgiNHjqBt27bQaDRISUmx+th3797F448/jrt37+KVV15B69atkZubi9OnTyM+Ph7Hjx9Hq1at7PZeSDp9+/bF4cOHUatWLUPZV199hQULFmD27Nno3bs3NBoNAGDFihVYtWoVli1bhrCwMPj5+UkVdpmSk5Mxb948jBo1ypCcuaLly5dLHcJDOX78OCZNmoSxY8di5MiRUKlUqFSpksm6FeX/hCoOJjcy1KJFC4SHh5vd/vHHH8PDo7jRbuLEiTYlN1988QV+//137N27F126dDHaNnXqVOj1+vIFXQ6FhYVQKBRQqVz711in06GoqMiQCFQU1atXR/Xq1Y3KfvnlFwDApEmTUKNGDaNyb29vTJw40W6vf//+ffj4+NjteBVNs2bNpA7hoZw8eRIA8MILL6Bdu3aSxJCbmwsvLy+2drgh3pZyQyWJTXncvHkTAIy+zVs69q+//oohQ4YgMDAQGo0GdevWxYgRI5Cfn2+o88svv6B///6oUqUKvLy80KZNG/z3v/81Os7+/fuhUCiwfv16TJs2DXXq1IFGo8Hvv/8OANi9eze6desGf39/+Pj4oEOHDtizZ4/F93L9+nWo1Wq88cYbpbb9+uuvUCgU+OCDDwxlWVlZGDduHIKCgqBWq1G/fn3MmzcPRUVFhjolTcyLFi3CW2+9hfr160Oj0WDfvn3Q6/V466230KRJE3h7e6Ny5cpo1aoVli5dath/1KhRqFevXql4TDVHf/HFF4iIiIBWq4WPjw8aNGiA0aNHW3zPAJCTk4MXXngBAQEB8PPzQ69evXD69OlS9R68LVWvXj28/vrrAIDAwEDDbSyFQoHVq1cjNzfXcBu0pIldCIHly5ejTZs28Pb2RpUqVTBw4ECcO3fO6LWeeOIJtGjRAt999x0iIyPh4+NjeC85OTmYPn066tevD7VajTp16mDy5Mm4d++e0TEUCgUmTpyI9evXIzQ0FD4+PmjdujW2b99udB5feeUVAED9+vUN8e7fv9/iOfv+++/Rr18/BAQEwMvLCw0bNsTkyZNL1bt69SqGDBkCrVaLwMBAjB49GtnZ2UZ18vLyMGvWLKP3M2HChFK3ZEzdlsrPz8f8+fMRGhoKLy8vBAQEoEuXLkhOTjbUsfacp6am4qmnnkKNGjWg0WhQu3Zt9O3bFxkZGRbPBQCsWbMGrVu3hpeXF6pWrYpnnnkGp06dMor9n//8JwAgIiICCoUCo0aNMnksa/9PduzYgUcffRTe3t5o2rQp1qxZY7S95Pd1165dGD16NKpXrw4fHx/DtWbTpk1o3749fH194efnh549eyI1NdXoGOfOncPzzz+P2rVrQ6PRIDAwEN26dcPx48dLxV1WPIB11zZzvvnmG7Rp0wYajQb169fHu+++a7Jeea8DsidINuLi4gQAceTIEVFYWGj0MGfChAnCll+DgwcPCgDiscceE1u2bBE3btwwW/f48ePCz89P1KtXT6xcuVLs2bNH/O9//xODBg0SOTk5Qgghfv31V1GpUiXRsGFDsW7dOvHNN9+IIUOGCABi4cKFhmPt27dPABB16tQRAwcOFNu2bRPbt28XN2/eFOvXrxcKhUIMGDBAxMfHi6+//lo89dRTQqlUit27d1t8P88884wIDg4WOp3OqHzGjBlCrVYb3l9mZqYIDg4WISEhYtWqVWL37t3izTffFBqNRowaNcqw3/nz5w1xdunSRWzevFns2rVLnD9/XsTGxgqlUinmzp0r9uzZI3bs2CGWLFkiYmJiDPuPHDlShISElIpz7ty5Rv9PycnJQqFQiOeff14kJCSIvXv3iri4ODF8+HCL71ev14suXboIjUYjFixYIHbt2iXmzp0rGjRoIACIuXPnGuqW/D6dP39eCCHEsWPHxJgxYwQAsWPHDnH48GFx6dIlcfjwYdGnTx/h7e0tDh8+LA4fPiyuXbsmhBDihRdeEJ6enmLatGlix44d4tNPPxVNmzYVgYGBIisry/BanTt3FlWrVhXBwcFi2bJlYt++fSIpKUncu3dPtGnTRlSrVk0sXrxY7N69WyxdulRotVrRtWtXodfrDccAIOrVqyfatWsnPv/8c5GQkCCeeOIJoVKpxNmzZ4UQQly6dEm89NJLAoCIj483xJudnW32nO3YsUN4enqKVq1aibVr14q9e/eKNWvWiOeff77U/0+TJk3EnDlzRGJioli8eLHQaDQiKirK6Pz37NlTqFQq8cYbb4hdu3aJd999V/j6+oq2bduKvLw8o3PSuXNnw8+FhYWiS5cuQqVSienTp4uEhASxbds28dprr4mNGzca6llzzu/evSsCAgJEeHi4+Pzzz0VSUpLYtGmTiI6OFmlpaRZ/h/79738LAGLIkCHim2++EevWrRMNGjQQWq1WnD59WgghxMmTJ8Xrr78uAIi4uDhx+PBh8fvvv5s8Xln/JyEhISIoKEg0a9ZMrFu3TuzcuVM899xzAoBISkoyHKfk97VOnTriX//6l/j222/F5s2bRVFRkViwYIFQKBRi9OjRYvv27SI+Pl60b99e+Pr6ipMnTxqO0aRJE/HII4+I9evXi6SkJPHll1+KadOmiX379hnqWBuPtde2kmtGXFycoWz37t1CqVSKf/zjHyI+Pl588cUX4rHHHhN169a1y3XAHTC5kZGSP25TD3MJjq3JjRBCzJ8/X6jVasOx69evL6Kjo8VPP/1kVK9r166icuXKhg86U55//nmh0WhEenq6UXnv3r2Fj4+PuH37thDir+SmU6dORvXu3bsnqlatKvr162dUrtPpROvWrUW7du0svpdt27YJAGLXrl2GsqKiIlG7dm3x7LPPGsrGjRsn/Pz8xMWLF432f/fddwUAwwWy5ELV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zxUKBQBAr9cbyry9vQ3lJduUSiVSUlKgVCqNjlVy62ns2LHo2bMnvvnmG+zatQuxsbF477338NJLL1n9un9/TQAQQpQq+/s2InJNbLkhIpfXtm1b6HQ6XLt2DY888ojRo2bNmoZ6wcHBiI6ORnx8PKZNm4aPP/7Y6tcIDQ3FwYMHjcqSk5MRGhpqsn6zZs3w008/ITc311B25MgRG98ZETkCkxsicnmNGzfGsGHDMGLECMTHx+P8+fP48ccfsXDhQsOIqMmTJ2Pnzp04f/48jh07hr1795pNTEx55ZVXsHbtWqxcuRJnzpzB4sWLER8fb7aT8NChQ+Hh4YExY8YgLS0NCQkJePfdd+3yfono4TC5IaIKIS4uDiNGjMC0adPQpEkTPP300/j+++8RHBwMoHg+nAkTJiA0NBS9evVCkyZNsHz5cquPP2DAACxduhTvvPMOmjdvjlWrViEuLg5PPPGEyfp+fn74+uuvkZaWhrZt22L27NlYuHChPd4qET0kheCNYyIiIpIRttwQERGRrDC5ISIiIllhckNERESywuSGiIiIZIXJDREREckKkxsiIiKSFSY3REREJCtMboiIiEhWmNwQERGRrDC5ISIiIllhckNERESywuSGiIiIZOX/AWCrREchnsooAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline \n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Calculate the F1 score using different values for the classification threshold, \n",
    "# and pick the threshold that resulted in the highest F1 score.\n",
    "highest_f1 = 0\n",
    "threshold_highest_f1 = 0\n",
    "\n",
    "f1_scores = []\n",
    "for id, threhold in enumerate(thresholds):\n",
    "    f1_score = 2*precisions[id]*recalls[id]/(precisions[id]+recalls[id])\n",
    "    f1_scores.append(f1_score)\n",
    "    if(f1_score > highest_f1):\n",
    "        highest_f1 = f1_score\n",
    "        threshold_highest_f1 = threhold\n",
    "print(\"Highest F1 score on Validation:\", highest_f1, \\\n",
    "      \", Threshold for the highest F1 score:\", threshold_highest_f1)\n",
    "\n",
    "# Let's plot the F1 score versus different choices of thresholds\n",
    "plt.plot([0.5, 0.5], [np.min(f1_scores), np.max(f1_scores)], linestyle='--')\n",
    "plt.plot(thresholds, f1_scores, marker='.')\n",
    "plt.title('F1 Score versus different choices of thresholds')\n",
    "plt.xlabel('Threshold')\n",
    "plt.ylabel('F1 Score')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "sagemaker-distribution:Python",
   "language": "python",
   "name": "conda-env-sagemaker-distribution-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
